//
// Created by lz on 2/27/21.
//


//__STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS are a workaround to allow C++ programs
// to use stdint.h macros specified in the C99 standard that aren't in the C++ standard.
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif

#include "net/TimerQueue.h"

#include "base/Logging.h"
#include "net/EventLoop.h"
#include "net/Timer.h"
#include "net/TimerId.h"

#include <sys/timerfd.h>
#include <unistd.h>

namespace lzweb
{
	namespace net
	{
		namespace detail
		{
			int createTimerfd()
			{
				int timerfd = timerfd_create(CLOCK_MONOTONIC,
					TFD_NONBLOCK | TFD_CLOEXEC);

				if (timerfd < 0)
				{
					LOG_SYSFATAL << "Failed in timerfd_create";
				}

				return timerfd;

			}

			//这里传进的Timestamp 必须大于 now() ?
			struct timespec howMuchTimeFromNow(TimeStamp when)
			{
				int64_t microseconds = when.microSecondsSinceEpoch()
					- TimeStamp::now().microSecondsSinceEpoch();
				if(microseconds < 100)
				{
					microseconds = 100;
				}

				struct timespec ts;
				ts.tv_sec = static_cast<time_t>(microseconds / TimeStamp::kMicroSecondsPerSecond);
				ts.tv_nsec = static_cast<long>((microseconds % TimeStamp::kMicroSecondsPerSecond) * 1000);

				return ts;

			}

			void readTimerfd(int timerfd, TimeStamp now)
			{
				uint64_t  howmany;

				ssize_t n = read(timerfd,& howmany, sizeof howmany);
				LOG_TRACE << "TimerQueue::handleRead() "<< howmany << " at " << now.toString();
				if (n != sizeof howmany)
				{
					LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
				}
			}

			void resetTimerfd(int timerfd, TimeStamp expiration)
			{
				// wake up loop by timerfd_settime()
				struct itimerspec newValue;
				struct itimerspec oldValue;

				memZero(&newValue, sizeof newValue);
				memZero(&oldValue, sizeof oldValue);

				newValue.it_value = howMuchTimeFromNow(expiration);
				int ret = timerfd_settime(timerfd, 0, &newValue, &oldValue);
				if(ret)
				{
					LOG_SYSERR << "timerfd_settime()";
				}
			}

		} // namespace detail
	} // namespace net
} // namespace lzweb

using namespace lzweb;
using namespace lzweb::net;
using namespace lzweb::net::detail;

TimerQueue::TimerQueue(EventLoop* loop)
:loop_(loop),
timerfd_(createTimerfd()),
timerfdChannel_(loop,timerfd_),
timers_(),
callingExpiredTimers_(false)
{
	timerfdChannel_.setReadCallback( std:: bind(&TimerQueue::handleRead, this));
	timerfdChannel_.enableReading();
}

TimerQueue::~TimerQueue()
{
	timerfdChannel_.disableAll();
	timerfdChannel_.remove();
	close(timerfd_);
	// do not remove channel, since we're in EventLoop::dtor();

	for(const Entry& timer: timers_)
	{
		delete timer.second;
	}
}

//向loop中添加一个timer，并返回代理该timer的timeId（里面存指向该timer指针和sequence）
TimerId TimerQueue::addTimer(TimerCallback cb, TimeStamp when, double interval)
{
	Timer* timer = new Timer(std::move(cb), when, interval);
	loop_->runInLoop(std::bind(&TimerQueue::addTimerInLoop, this, timer));

	return TimerId(timer,timer->sequence());
}

// 根据一个timerId去除对应的timer？
void TimerQueue::cancel(TimerId timerId)
{
	loop_->runInLoop(
		std::bind(&TimerQueue::cancelInLoop, this, timerId)
	);
}

void TimerQueue::addTimerInLoop(Timer* timer)
{
	loop_->assertInLoopThread();
	bool earliestChanged = insert(timer);

	if(earliestChanged)
	{
		resetTimerfd(timerfd_,timer->expiration());
	}
}

void TimerQueue::cancelInLoop(TimerId timerId)
{
	loop_->assertInLoopThread();
	assert(timers_.size() == activeTimers_.size());
	// TimerQueue 是 TimerId的友类
	ActiveTimer timer(timerId.timer_,timerId.sequence_);
	auto it = activeTimers_.find(timer);
	if(it != activeTimers_.end())
	{
		size_t n = timers_.erase(Entry(it->first->expiration(),it->first));
		assert(n == 1); (void) n;

		delete it->first;
		activeTimers_.erase(it);
	}
	else if (callingExpiredTimers_)
	{
		cancelingTimers_.insert(timer);
	}
	assert(timers_.size() == activeTimers_.size());


}

//找到过期的timer，并运行它们的处理函数
void TimerQueue::handleRead()
{
	loop_->assertInLoopThread();
	TimeStamp now(TimeStamp::now());
	readTimerfd(timerfd_, now);
	std::vector<Entry> expired = getExpired(now);

	callingExpiredTimers_ = true;
	cancelingTimers_.clear();

	for(const Entry& it: expired)
	{
		it.second->run();
	}
	callingExpiredTimers_ = false;

	reset(expired, now);

}

//从未过期的timerList中截取已过期的timer
std::vector<TimerQueue::Entry> TimerQueue::getExpired(TimeStamp now)
{
	assert(timers_.size() == activeTimers_.size());
	std::vector<Entry> expired;
	Entry sentry(now,reinterpret_cast<Timer*>(UINTPTR_MAX));
	//lower_bound finds >= in [x,y), and upper_bound finds > in [x,y)
	TimerList::iterator end = timers_.lower_bound(sentry);
	assert(end == timers_.end() || now < end->first);

	// copy range -> [first, last)
	std::copy(timers_.begin(),end,back_inserter(expired));
	timers_.erase(timers_.begin(), end);


	for(const Entry& it: expired)
	{
		ActiveTimer timer(it.second, it.second->sequence());
		size_t n = activeTimers_.erase(timer);
		assert(n == 1); (void) n;
	}

	assert(timers_.size() == activeTimers_.size());
	return expired;
}

//对需要重复的时间器进行重置，并重置timerfd下次触发的时间
void TimerQueue::reset(const std::vector<Entry>& expired, TimeStamp now)
{
	TimeStamp nextExpire;
	for (const Entry& it: expired)
	{
		ActiveTimer timer(it.second, it.second->sequence());
		if(it.second->repeat() && cancelingTimers_.find(timer) == cancelingTimers_.end())
		{
			it.second->restart(now);
			insert(it.second);
		}
		else
		{
			///FIXME move to a free list
			delete it.second;   // FIXME: no delete please
		}
	}

	if (!timers_.empty())
	{
		nextExpire = timers_.begin()->second->expiration();
	}

	if (nextExpire.valid())
	{
		resetTimerfd(timerfd_, nextExpire);
	}
}

bool TimerQueue::insert(Timer* timer)
{
	loop_->assertInLoopThread();
	assert(timers_.size() == activeTimers_.size());
	bool earliestChanged = false;
	TimeStamp when = timer->expiration();
	auto it = timers_.begin();
	if(it == timers_.end()||when < it->first)
	{
		earliestChanged = true;
	}
	{
		std::pair<TimerList::iterator, bool> result
			= timers_.insert(Entry(when, timer));
		assert(result.second);
		(void)result;
	}
	{
		std::pair<ActiveTimerSet::iterator, bool> result
			= activeTimers_.insert(ActiveTimer(timer, timer->sequence()));

		assert(result.second);
		(void) result;
	}

	assert(timers_.size() == activeTimers_.size());
	return earliestChanged;

}






